﻿using BestHTTP.WebSocket;
using System;
using System.Threading;
using UnityEngine;

namespace Shioho.Net
{
    public class BestHttpWebsocket 
    {
        public bool IsSilentReconnect = true;

        private WebSocket _webSocket = null;
        private string _url;
        //静默重连
        private Timer _silentReconnectTimer;
        private int _curSilentReconnectCnt;
        private const int _silentReconnectDefaultTime = 3000;
        private const int _maxSilentReconnectCnt = 10;
        private bool _isConnectSign;

        protected virtual void OnOpen() { }
        protected virtual void OnClosed() { }
        protected virtual void OnError() { }
        protected virtual void OnSilentReconnect() { }
        protected virtual void OnReceivedBinary(byte[] buffer) { }
        protected virtual void OnReceivedString(string message) { }

        public BestHttpWebsocket(string url)
        {
            _url = url;
        }

        string GetWsUrl()
        {
            return _url;
        }

        public void Connect()
        {
            _isConnectSign = true;
            CreateWebSocket();
        }

        public void Disconnect()
        {
            _isConnectSign = false;
            _curSilentReconnectCnt = 0;
            CloseSilentReconnectTimer();
            CloseWebSocket();
        }

        public void Reconnect()
        {
            Disconnect();
            Connect();
        }

        public bool IsOpen()
        {
            return _webSocket != null && _webSocket.State == WebSocketStates.Open;
        }

        public void Request(byte[] msg)
        {
            _webSocket?.Send(msg);
        }

        public void Request(string str)
        {
            _webSocket?.Send(str);
        }

        protected void SilentReconnect()
        {
            //静默重连
            CloseSilentReconnectTimer();
            _silentReconnectTimer = new Timer(new TimerCallback(SilentReconnectTimer), null, _silentReconnectDefaultTime, _silentReconnectDefaultTime);
        }

        private void CreateWebSocket()
        {
            if (_webSocket != null)
                return;

            var uri = new Uri(_url);
            _webSocket = new WebSocket(uri);

            _webSocket.OnOpen = Open;
            _webSocket.OnMessage = MessageReceived;
            _webSocket.OnBinary = BinaryReceived;
            _webSocket.OnClosed = Closed;
            _webSocket.OnError = Error;

            _webSocket.StartPingThread = false;
            _webSocket.Open();
        }

        private void CloseWebSocket()
        {
            _webSocket?.Close();
            _webSocket = null;
        }

        private void BinaryReceived(WebSocket ws, byte[] buffer)
        {
            OnReceivedBinary(buffer);
        }

        private void MessageReceived(WebSocket ws, string message)
        {
            OnReceivedString(message);
        }

        private void Open(WebSocket ws)
        {
            _curSilentReconnectCnt = 0;
            CloseSilentReconnectTimer();

            Debug.Log($"{GetType().Name} Open");
            OnOpen();
        }

        private void Closed(WebSocket ws, UInt16 code, string message)
        {
            Debug.Log($"{GetType().Name} Closed: Code:{code} Message:{message}");
            OnClosed();
        }

        private void Error(WebSocket ws, string ex)
        {
            string errorMsg = string.Empty;
#if !UNITY_WEBGL || UNITY_EDITOR
            if (ws.InternalRequest.Response != null)
                errorMsg = string.Format("Status Code from Server: {0} and Message: {1}", ws.InternalRequest.Response.StatusCode, ws.InternalRequest.Response.Message);
#endif
            Debug.LogError($"{GetType().Name} Error:  {errorMsg}");
            TrySilentReconnect();
            OnError();
        }

        private void TrySilentReconnect()
        {
            if (!_isConnectSign || !IsSilentReconnect)
                return;

            SilentReconnect();
        }

        private void CloseSilentReconnectTimer()
        {
            _silentReconnectTimer?.Dispose();
            _silentReconnectTimer = null;
        }

        private void SilentReconnectTimer(object state)
        {
            _curSilentReconnectCnt++;
            if (_curSilentReconnectCnt >= _maxSilentReconnectCnt)
            {
                Disconnect();
                Debug.Log("静默重连次数超出，不再尝试...");
            }
            else
            {
                CloseSilentReconnectTimer();

                CloseWebSocket();
                OnSilentReconnect();
                CreateWebSocket();
            }
        }

    }
}

